package com.example.iso9797demo;

import org.bouncycastle.crypto.macs.ISO9797Alg3Mac;
import org.bouncycastle.crypto.engines.DESEngine;
import org.bouncycastle.crypto.paddings.*;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.junit.*;
import org.junit.runners.MethodSorters;
import static org.junit.Assert.*;

import java.util.Arrays;

/**
 * ISO9797Alg3Mac 单元测试（Android版）
 * 对标 HarmonyOS 测试用例，确保跨平台一致性
 * 
 * 运行方式：
 * 1. Android Studio: 右键点击类名 -> Run "ISO9797Alg3MacTest"
 * 2. 命令行: cd android-demo && ./gradlew test
 * 3. 查看日志: adb logcat | grep "ISO9797Test"
 * 4. 查看报告: android-demo/app/build/reports/tests/testDebugUnitTest/index.html
 */
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ISO9797Alg3MacTest {
    
    private static final String TAG = "ISO9797Alg3Mac::";
    
    private byte[] testInput, testKey16, testKey24, testIV, expectedMac;
    
    @BeforeClass
    public static void setUpClass() {
        System.out.println(TAG + "========================================");
        System.out.println(TAG + "ISO9797Alg3Mac 测试套件开始");
        System.out.println(TAG + "========================================");
    }
    
    @Before
    public void setUp() {
        System.out.println(TAG + "测试用例开始");
        
        // 标准测试数据（与 HarmonyOS 完全一致）
        testInput = new byte[]{(byte)0x84, (byte)0xE4, (byte)0x00, (byte)0x80, (byte)0x0F, (byte)0x4F, (byte)0x05, (byte)0x47, (byte)0x4D, (byte)0x50, (byte)0x4B, (byte)0x49};
        testKey16 = new byte[]{(byte)0x31, (byte)0x9C, (byte)0xD0, (byte)0x5D, (byte)0x11, (byte)0xBB, (byte)0x06, (byte)0x72, (byte)0x9B, (byte)0xE2, (byte)0x3D, (byte)0xA1, (byte)0x9B, (byte)0x80, (byte)0xE6, (byte)0xA3};
        testKey24 = new byte[]{(byte)0x31, (byte)0x9C, (byte)0xD0, (byte)0x5D, (byte)0x11, (byte)0xBB, (byte)0x06, (byte)0x72, (byte)0x9B, (byte)0xE2, (byte)0x3D, (byte)0xA1, (byte)0x9B, (byte)0x80, (byte)0xE6, (byte)0xA3, (byte)0x7F, (byte)0x1A, (byte)0x2B, (byte)0x3C, (byte)0x4D, (byte)0x5E, (byte)0x6F, (byte)0x70};
        testIV = new byte[]{(byte)0xA0, (byte)0x97, (byte)0x99, (byte)0x41, (byte)0x08, (byte)0xD0, (byte)0x6F, (byte)0x1B};
        expectedMac = new byte[]{(byte)0xF0, (byte)0xE6, (byte)0xEF, (byte)0x61, (byte)0x39, (byte)0xD2, (byte)0x37, (byte)0x33};
    }
    
    @After
    public void tearDown() {
        System.out.println(TAG + "测试用例结束");
    }
    
    @AfterClass
    public static void tearDownClass() {
        System.out.println(TAG + "========================================");
        System.out.println(TAG + "ISO9797Alg3Mac 测试套件结束");
        System.out.println(TAG + "========================================");
    }
    
    private String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) sb.append(String.format("%02X", b & 0xFF));
        return sb.toString();
    }
    
    // ========== 基础功能测试 ==========
    
    @Test
    public void test01() {
        System.out.println(TAG + "[测试01] 构造函数_无参数");
        ISO9797Alg3Mac mac = new ISO9797Alg3Mac(new DESEngine());
        System.out.println(TAG + "[测试01] 构造函数_无参数 实例化成功");
        System.out.println(TAG + "[测试01] 算法名称: " + mac.getAlgorithmName());
        System.out.println(TAG + "[测试01] MAC大小: " + mac.getMacSize() + " 字节");
        System.out.println(TAG + "[测试01] 填充方式: 零填充(默认)");
        assertEquals("ISO9797Alg3", mac.getAlgorithmName());
        assertEquals(8, mac.getMacSize());
        System.out.println(TAG + "[测试01] 无参数构造函数配置验证通过");
        System.out.println(TAG + "✓ 测试01 通过");
    }
    
    @Test
    public void test02() {
        System.out.println(TAG + "[测试02] 构造函数_填充参数");
        ISO7816d4Padding padding = new ISO7816d4Padding();
        ISO9797Alg3Mac mac = new ISO9797Alg3Mac(new DESEngine(), padding);
        System.out.println(TAG + "[测试02] 构造函数_填充参数 实例化成功");
        System.out.println(TAG + "[测试02] 算法名称: " + mac.getAlgorithmName());
        System.out.println(TAG + "[测试02] MAC大小: " + mac.getMacSize() + " 字节");
        System.out.println(TAG + "[测试02] 填充方式: " + padding.getClass().getSimpleName());
        assertEquals(8, mac.getMacSize());
        System.out.println(TAG + "[测试02] 填充参数构造函数配置验证通过");
        System.out.println(TAG + "✓ 测试02 通过");
    }
    
    @Test
    public void test03() {
        System.out.println(TAG + "[测试03] 初始化_16字节密钥");
        ISO9797Alg3Mac mac = new ISO9797Alg3Mac(new DESEngine(), new ISO7816d4Padding());
        mac.init(new KeyParameter(testKey16));
        System.out.println(TAG + "[测试03] 初始化_16字节密钥  算法名称是否一致: " + (mac.getAlgorithmName().equals("ISO9797Alg3")));
        assertEquals("ISO9797Alg3", mac.getAlgorithmName());
        System.out.println(TAG + "✓ 测试03 通过");
    }
    
    @Test
    public void test04() {
        System.out.println(TAG + "[测试04] 初始化_带IV参数");
        ISO9797Alg3Mac mac = new ISO9797Alg3Mac(new DESEngine(), new ISO7816d4Padding());
        mac.init(new ParametersWithIV(new KeyParameter(testKey16), testIV));
        System.out.println(TAG + "[测试04] 初始化_带IV参数  算法名称是否一致: " + (mac.getAlgorithmName().equals("ISO9797Alg3")));
        assertEquals("ISO9797Alg3", mac.getAlgorithmName());
        System.out.println(TAG + "✓ 测试04 通过");
    }
    
    @Test
    public void test05() {
        System.out.println(TAG + "[测试05] ISO7816d4Padding_标准测试向量");
        ISO9797Alg3Mac mac = new ISO9797Alg3Mac(new DESEngine(), new ISO7816d4Padding());
        mac.init(new ParametersWithIV(new KeyParameter(testKey16), testIV));
        mac.update(testInput, 0, testInput.length);
        byte[] output = new byte[8];
        mac.doFinal(output, 0);
        
        String result = bytesToHex(output);
        String expected = bytesToHex(expectedMac);
        System.out.println(TAG + "[测试05] ISO7816d4Padding_标准测试向量是否一致: " + result.equals(expected));
        
        assertEquals(expected, result);
        System.out.println(TAG + "✓ 测试05 通过");
    }
    
    @Test
    public void test06() {
        System.out.println(TAG + "[测试06] ZeroBytePadding_基本计算");
        ISO9797Alg3Mac mac = new ISO9797Alg3Mac(new DESEngine(), new ZeroBytePadding());
        mac.init(new ParametersWithIV(new KeyParameter(testKey16), testIV));
        mac.update(testInput, 0, testInput.length);
        byte[] output = new byte[8];
        mac.doFinal(output, 0);
        
        String hexOutput = bytesToHex(output);
        System.out.println(TAG + "[测试06] ZeroBytePadding 输出(Hex): " + hexOutput);
        assertEquals(8, output.length);
        System.out.println(TAG + "✓ 测试06 通过");
    }
    
    @Test
    public void test07() {
        System.out.println(TAG + "[测试07] PKCS7Padding_基本计算");
        ISO9797Alg3Mac mac = new ISO9797Alg3Mac(new DESEngine(), new PKCS7Padding());
        mac.init(new ParametersWithIV(new KeyParameter(testKey16), testIV));
        mac.update(testInput, 0, testInput.length);
        byte[] output = new byte[8];
        mac.doFinal(output, 0);
        
        String hexOutput = bytesToHex(output);
        System.out.println(TAG + "[测试07] PKCS7Padding 输出(Hex): " + hexOutput);
        assertEquals(8, output.length);
        System.out.println(TAG + "✓ 测试07 通过");
    }
    
    @Test
    public void test08() {
        System.out.println(TAG + "[测试08] ISO10126d2Padding_随机填充");
        // ISO10126d2Padding 需要 SecureRandom
        java.security.SecureRandom random = new java.security.SecureRandom();
        ISO10126d2Padding padding = new ISO10126d2Padding();
        padding.init(random);
        
        ISO9797Alg3Mac mac = new ISO9797Alg3Mac(new DESEngine(), padding);
        mac.init(new ParametersWithIV(new KeyParameter(testKey16), testIV));
        mac.update(testInput, 0, testInput.length);
        byte[] output = new byte[8];
        mac.doFinal(output, 0);
        
        String hexOutput = bytesToHex(output);
        System.out.println(TAG + "[测试08] ISO10126d2Padding 输出(Hex,随机): " + hexOutput);
        assertEquals(8, output.length);
        System.out.println(TAG + "✓ 测试08 通过");
    }
    
    @Test
    public void test09() {
        System.out.println(TAG + "[测试09] ISO10126d2Padding_固定填充测试");
        // 使用固定的填充数据进行测试，确保跨平台一致性
        // 手动构造固定填充的数据：原始12字节 + 固定4字节填充(0xAA, 0xBB, 0xCC, 0x04)
        byte[] paddedInput = new byte[16];
        System.arraycopy(testInput, 0, paddedInput, 0, testInput.length);  // 前12字节：原始数据
        paddedInput[12] = (byte)0xAA;  // 固定填充字节1
        paddedInput[13] = (byte)0xBB;  // 固定填充字节2
        paddedInput[14] = (byte)0xCC;  // 固定填充字节3
        paddedInput[15] = (byte)0x04;  // 填充长度
        
        // 使用 ZeroBytePadding 避免二次填充，直接处理已填充的数据
        ISO9797Alg3Mac macFixed = new ISO9797Alg3Mac(new DESEngine(), new ZeroBytePadding());
        macFixed.init(new ParametersWithIV(new KeyParameter(testKey16), testIV));
        macFixed.update(paddedInput, 0, paddedInput.length);
        byte[] output = new byte[8];
        macFixed.doFinal(output, 0);
        
        String hexOutput = bytesToHex(output);
        System.out.println(TAG + "[测试09] ISO10126d2Padding 输出(Hex,固定AA-BB-CC-04): " + hexOutput);
        assertEquals(8, output.length);
        System.out.println(TAG + "✓ 测试09 通过");
    }
    
    @Test
    public void test10() {
        System.out.println(TAG + "[测试10] X923Padding_默认模式");
        ISO9797Alg3Mac mac = new ISO9797Alg3Mac(new DESEngine(), new X923Padding());
        mac.init(new ParametersWithIV(new KeyParameter(testKey16), testIV));
        mac.update(testInput, 0, testInput.length);
        byte[] output = new byte[8];
        mac.doFinal(output, 0);
        
        String hexOutput = bytesToHex(output);
        System.out.println(TAG + "[测试10] X923Padding 输出(Hex): " + hexOutput);
        assertEquals(8, output.length);
        System.out.println(TAG + "✓ 测试10 通过");
    }
    
    @Test
    public void test11() {
        System.out.println(TAG + "[测试11] TBCPadding_基本计算");
        ISO9797Alg3Mac mac = new ISO9797Alg3Mac(new DESEngine(), new TBCPadding());
        mac.init(new ParametersWithIV(new KeyParameter(testKey16), testIV));
        mac.update(testInput, 0, testInput.length);
        byte[] output = new byte[8];
        mac.doFinal(output, 0);
        
        String hexOutput = bytesToHex(output);
        System.out.println(TAG + "[测试11] TBCPadding 输出(Hex): " + hexOutput);
        assertEquals(8, output.length);
        System.out.println(TAG + "✓ 测试11 通过");
    }
    
    @Test
    public void test12() {
        System.out.println(TAG + "[测试12] 分段更新_结果一致性");
        ISO9797Alg3Mac mac1 = new ISO9797Alg3Mac(new DESEngine(), new ISO7816d4Padding());
        ISO9797Alg3Mac mac2 = new ISO9797Alg3Mac(new DESEngine(), new ISO7816d4Padding());
        ParametersWithIV params = new ParametersWithIV(new KeyParameter(testKey16), testIV);
        
        mac1.init(params);
        mac1.update(testInput, 0, testInput.length);
        byte[] output1 = new byte[8];
        mac1.doFinal(output1, 0);
        
        mac2.init(params);
        mac2.update(testInput, 0, 6);
        mac2.update(testInput, 6, testInput.length - 6);
        byte[] output2 = new byte[8];
        mac2.doFinal(output2, 0);
        
        System.out.println(TAG + "[测试12] 分段更新_结果一致性: output1 与 output2 一致： " + bytesToHex(output1).equals(bytesToHex(output2)) + " ");
        assertEquals(bytesToHex(output1), bytesToHex(output2));
        System.out.println(TAG + "✓ 测试12 通过");
    }
    
    @Test
    public void test13() {
        System.out.println(TAG + "[测试13] 24字节密钥_基本计算");
        ISO9797Alg3Mac mac = new ISO9797Alg3Mac(new DESEngine(), new ISO7816d4Padding());
        mac.init(new ParametersWithIV(new KeyParameter(testKey24), testIV));
        mac.update(testInput, 0, testInput.length);
        byte[] output = new byte[8];
        mac.doFinal(output, 0);
        
        String hexOutput = bytesToHex(output);
        System.out.println(TAG + "[测试13] 24字节密钥 输出(Hex): " + hexOutput);
        assertEquals(8, output.length);
        System.out.println(TAG + "✓ 测试13 通过");
    }
    
    @Test
    public void test14() {
        System.out.println(TAG + "[测试14] 不同MAC大小配置");
        // 测试不同的macSize配置，验证算法是否能正确处理不同大小的MAC
        System.out.println(TAG + "[测试14] 不同MAC大小配置");
        
        // 测试默认MAC大小(8字节)
        ISO9797Alg3Mac macDefault = new ISO9797Alg3Mac(new DESEngine(), new ISO7816d4Padding());
        assertEquals(8, macDefault.getMacSize());
        System.out.println(TAG + "[测试14] 默认MAC大小: " + macDefault.getMacSize());
        
        // 测试4字节MAC大小
        ISO9797Alg3Mac mac4Bytes = new ISO9797Alg3Mac(new DESEngine(), 32, new ISO7816d4Padding()); // 32位 = 4字节
        assertEquals(4, mac4Bytes.getMacSize());
        System.out.println(TAG + "[测试14] 4字节MAC大小: " + mac4Bytes.getMacSize());
        
        // 测试3字节MAC大小
        ISO9797Alg3Mac mac3Bytes = new ISO9797Alg3Mac(new DESEngine(), 24, new ISO7816d4Padding()); // 24位 = 3字节
        assertEquals(3, mac3Bytes.getMacSize());
        System.out.println(TAG + "[测试14] 3字节MAC大小: " + mac3Bytes.getMacSize());
        
        // 使用不同大小的MAC进行计算
        ParametersWithIV params = new ParametersWithIV(new KeyParameter(testKey16), testIV);
        
        // 默认大小MAC计算
        macDefault.init(params);
        macDefault.update(testInput, 0, testInput.length);
        byte[] outputDefault = new byte[macDefault.getMacSize()];
        macDefault.doFinal(outputDefault, 0);
        assertEquals(8, outputDefault.length);
        System.out.println(TAG + "[测试14] 默认大小MAC输出(Hex): " + bytesToHex(outputDefault));
        
        // 4字节MAC计算
        mac4Bytes.init(params);
        mac4Bytes.update(testInput, 0, testInput.length);
        byte[] output4Bytes = new byte[mac4Bytes.getMacSize()];
        mac4Bytes.doFinal(output4Bytes, 0);
        assertEquals(4, output4Bytes.length);
        System.out.println(TAG + "[测试14] 4字节MAC输出(Hex): " + bytesToHex(output4Bytes));
        
        // 3字节MAC计算
        mac3Bytes.init(params);
        mac3Bytes.update(testInput, 0, testInput.length);
        byte[] output3Bytes = new byte[mac3Bytes.getMacSize()];
        mac3Bytes.doFinal(output3Bytes, 0);
        assertEquals(3, output3Bytes.length);
        System.out.println(TAG + "[测试14] 3字节MAC输出(Hex): " + bytesToHex(output3Bytes));
        
        System.out.println(TAG + "✓ 测试14 通过");
    }
    
    @Test
    public void test15() {
        System.out.println(TAG + "[测试15] 构造函数_MAC大小参数");
        ISO9797Alg3Mac mac = new ISO9797Alg3Mac(new DESEngine(), 32); // 32位 = 4字节
        System.out.println(TAG + "[测试15] 构造函数_MAC大小参数 实例化成功");
        System.out.println(TAG + "[测试15] 算法名称: " + mac.getAlgorithmName());
        System.out.println(TAG + "[测试15] MAC大小: " + mac.getMacSize() + " 字节");
        System.out.println(TAG + "[测试15] 填充方式: 零填充(默认)");
        assertEquals(4, mac.getMacSize());
        System.out.println(TAG + "[测试15] MAC大小参数构造函数配置验证通过");
        System.out.println(TAG + "✓ 测试15 通过");
    }

    @Test
    public void test16() {
        System.out.println(TAG + "[测试16] 构造函数_MAC大小和填充参数");
        PKCS7Padding padding = new PKCS7Padding();
        ISO9797Alg3Mac mac = new ISO9797Alg3Mac(new DESEngine(), 32, padding); // 32位 = 4字节 + PKCS7填充
        System.out.println(TAG + "[测试16] 构造函数_MAC大小和填充参数 实例化成功");
        System.out.println(TAG + "[测试16] 算法名称: " + mac.getAlgorithmName());
        System.out.println(TAG + "[测试16] MAC大小: " + mac.getMacSize() + " 字节");
        System.out.println(TAG + "[测试16] 填充方式: " + padding.getClass().getSimpleName());
        assertEquals(4, mac.getMacSize());
        System.out.println(TAG + "[测试16] MAC大小和填充参数构造函数配置验证通过");
        System.out.println(TAG + "✓ 测试16 通过");
    }
    
    @Test
    public void test17() {
        System.out.println(TAG + "[测试17] 单字节更新接口测试");
        ISO9797Alg3Mac mac1 = new ISO9797Alg3Mac(new DESEngine(), new ISO7816d4Padding());
        ISO9797Alg3Mac mac2 = new ISO9797Alg3Mac(new DESEngine(), new ISO7816d4Padding());
        ParametersWithIV params = new ParametersWithIV(new KeyParameter(testKey16), testIV);
        
        // 使用update方法更新数据
        mac1.init(params);
        mac1.update(testInput, 0, testInput.length);
        byte[] output1 = new byte[8];
        mac1.doFinal(output1, 0);
        
        // 使用update方法逐字节更新数据（模拟updateByte方法）
        mac2.init(params);
        for (int i = 0; i < testInput.length; i++) {
            mac2.update(testInput, i, 1);
        }
        byte[] output2 = new byte[8];
        mac2.doFinal(output2, 0);
        
        // 验证两种方式结果一致
        String hexOutput1 = bytesToHex(output1);
        String hexOutput2 = bytesToHex(output2);
        System.out.println(TAG + "[测试17] update方法输出(Hex): " + hexOutput1);
        System.out.println(TAG + "[测试17] update逐字节方法输出(Hex): " + hexOutput2);
        assertEquals(hexOutput1, hexOutput2);
        System.out.println(TAG + "[测试17] 单字节更新接口测试通过");
        System.out.println(TAG + "✓ 测试17 通过");
    }
}
